1 module hip.api.graphics.text;
2 import hip.api.data.font;
3 public import hip.math.rect : Size;
4 
5 /**
6  * All the combinations that exists for align, specifies firstly the vertical position, then the horizontal:
7  * i.e: centerLeft is center vertical, left horizontally
8  */
9 enum HipTextAlign : ubyte
10 {
11     ///This means leftCenter internally
12     defaultAlign = 0,
13     centerH = 0b000001,
14     left    = 0b000010,
15     right   = 0b000100,
16     centerV = 0b001000,
17     top     = 0b010000,
18     bottom  = 0b100000,
19 
20 
21     topCenter  = top | centerH,
22     topLeft    = top | left,
23     topRight   = top | right,
24 
25     center     = centerV | centerH,
26     centerLeft = centerV | left,
27     centerRight= centerV | right,
28 
29     botCenter  = bottom | centerH,
30     botLeft    = bottom | left,
31     botRight   = bottom | right,
32 
33     horizontalMask = centerH | left | right,
34     verticalMask = centerV | top | bottom,
35 }
36 
37 HipTextAlign getAlignH(HipTextAlign input)
38 {
39     return cast(HipTextAlign)(input & HipTextAlign.horizontalMask);
40 }
41 
42 HipTextAlign getAlignV(HipTextAlign input)
43 {
44     return cast(HipTextAlign)(input & HipTextAlign.verticalMask);
45 }
46 
47 /**
48  *
49  * Params:
50  *   x = Specified X
51  *   y = Specified Y
52  *   width = The width of the subject
53  *   height = The height of the subject
54  *   alignment = Alignment for calculating newX and newY
55  *   newX = Out arg
56  *   newY = Out arg
57  *   bounds = The bounds to align to. If It is == 0, it won't be considered in the calculation
58  */
59 void getPositionFromAlignment(
60     int x, int y, int width, int height, HipTextAlign alignment,
61     out int newX, out int newY, Size bounds
62 )
63 {
64     with(HipTextAlign)
65     {
66         switch(getAlignH(alignment))
67         {
68             case centerH:
69                 if(bounds.width != 0)
70                     newX = (x + (bounds.width)/2) - (width / 2);
71                 else
72                     newX = x - width/2;
73                 break;
74             case right:
75                 if(bounds.width != 0)
76                     newX = x + bounds.width - width;
77                 else
78                     newX = x - width;
79                 break;
80             case left:
81                 newX = x;
82                 break;
83             default: assert(false, "Some invalid horizontal alignment was received.");
84         }
85         switch(getAlignV(alignment))
86         {
87             case centerV:
88                 if(bounds.height != 0)
89                     newY = y + (bounds.height/2) - height/2;
90                 else
91                     newY = y- height/2;
92                 break;
93             case bottom:
94                 if(bounds.height != 0)
95                     newY = y + bounds.height - height;
96                 else
97                     newY = y - height;
98                 break;
99             case top:
100                 newY = y;
101                 break;
102             default: assert(false, "Some invalid vertical alignment was received.");
103         }
104     }
105 }
106 
107 /**
108  *
109  * Params:
110  *   font = The font used as a reference to build the quad
111  *   vertices = Output vertices
112  *   text = The text to build the vertices
113  *   x = Start X
114  *   y = Start Y
115  *   depth = Depth for possibly Z buffer
116  *   scale = Rescaling the font
117  *   align_ = Alignment on where it will show
118  *   bounds = -1 value will make it be ignored. If exists, it will be used both for word wrap and for alignment to the size specified
119  *   wordWrap = If the text will line-break if it reaches the size too big
120  *   shouldRenderSpace = Render ' ' characters or not.
121  * Returns:
122  */
123 int putTextVertices(
124     IHipFont font, HipTextRendererVertexAPI[] vertices,
125     string text,
126     int x, int y, float depth, float scale = 1.0f,
127     HipTextAlign align_ = HipTextAlign.center,
128     Size bounds = Size.init,
129     bool wordWrap = false,
130     bool shouldRenderSpace
131 )
132 {
133     int yoffset = 0;
134     bool isFirstLine = true;
135     int vI = 0;
136     int height = cast(int)(font.getTextHeight(text) * scale);
137 
138     foreach(HipLineInfo lineInfo; font.wordWrapRange(text, wordWrap ? bounds.width : -1))
139     {
140         if(!isFirstLine)
141         {
142             yoffset+= font.lineBreakHeight * scale;
143         }
144         isFirstLine = false;
145         int xoffset = 0;
146         int displayX = void, displayY = void;
147         int lineYOffset = 0;
148         // if(align_ & HipTextAlign.top) lineYOffset = yoffset - cast(int)(lineInfo.minYOffset * scale);
149         lineYOffset =  -cast(int)(lineInfo.minYOffset * scale);
150 
151         getPositionFromAlignment(
152             x, y,
153             cast(int)(lineInfo.width * scale), cast(int)(height ? height : lineInfo.height * scale),
154             align_,
155             displayX, displayY,
156             bounds
157         );
158         for(int i = 0; i < lineInfo.line.length; i++)
159         {
160             int kerning = lineInfo.kerningCache[i];
161             const(HipFontChar)* ch = lineInfo.fontCharCache[i];
162 
163             switch(lineInfo.line[i])
164             {
165                 case ' ':
166                     if(!shouldRenderSpace)
167                     {
168                         xoffset+= font.spaceWidth * scale;
169                         break;
170                     }
171                     goto default;
172                 default:
173                     if(ch is null) continue;
174                     ch.putCharacterQuad(
175                         cast(float)(xoffset+displayX+(ch.xoffset+kerning) *scale),
176                         cast(float)(yoffset+displayY+lineYOffset + ch.yoffset*scale), depth,
177                         vertices[vI..vI+4], scale
178                     );
179                     vI+= 4;
180                     xoffset+= ch.xadvance*scale;
181             }
182         }
183     }
184     return vI;
185 }